using NLog;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace TPXSDK.NET.Link;

/// <inheritdoc />
/// <summary>
/// UDP Client communication adapter,配置字符串格式为：
/// UDPC:目标IP:目标端口号
/// </summary>
public class UdpClientAdapter : CommAdapter
{
    #region 成员变量
    private readonly Logger log = LogManager.GetLogger("UdpClientAdapter");
    private bool _isRunning;
    private readonly byte[] _rcvData = new byte[4096];
    private Socket? _socket;
    private Thread? _socketRcvThread;
    private readonly object _locker = new();
    private readonly List<object> _refObjs = [];
    private readonly IPAddress _remoteIp;
    private readonly int _remotePort;
    private readonly EndPoint _remoteEp;
    #endregion

    /// <summary>
    /// Initializes a new instance of the <see cref="UdpClientAdapter"/> class.
    /// </summary>
    /// <param name="strDesc">适配器参数描述字符串</param>
    public UdpClientAdapter(string strDesc)
    {
        var strPara = strDesc.Split(':');
        if (strPara.Length != 3 || "UDPC" != strPara[0])
        {
            throw new Exception($"Invalid UdpClient {strDesc}");
        }
        _remoteIp = IPAddress.Parse(strPara[1]);
        _remotePort = Convert.ToInt32(strPara[2]);
        _remoteEp = new IPEndPoint(_remoteIp, _remotePort);

    }

    /// <inheritdoc />
    /// <summary>
    /// 打开通讯适配器
    /// </summary>
    public override bool Open()
    {
        // 启动一个新的线程，执行数据接收的操作
        if (_isRunning) return true;

        // 得到本机IP，设置TCP/UDP 端口号
        try
        {
            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

            uint IOC_IN = 0x80000000;
            uint IOC_VENDOR = 0x18000000;
            uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
            _socket.IOControl((int)SIO_UDP_CONNRESET, [Convert.ToByte(false)], null);

            _socket.Bind(new IPEndPoint(IPAddress.Any, 0));
            _socketRcvThread = new Thread(SocketReceiveHandle) { IsBackground = true };
            _socketRcvThread.Start();
            _isRunning = true;
        }
        catch (Exception e)
        {
            _isRunning = false;
            log.Error($"UDP open error: {e}");
            NotifyCommError($"UDP open error: {e}");
        }

        return _isRunning;
    }

    /// <inheritdoc />
    /// <summary>
    /// 关闭适配器
    /// </summary>
    public override void Close()
    {
        _isRunning = false;
    }
    
    /// <summary>
    /// 添加引用对象，返回当前引用对象个数
    /// </summary>
    public override int AddRefObj(object user)
    {
        if (!_refObjs.Contains(user))
        {
            _refObjs.Add(user);
        }
        return _refObjs.Count;
    }

    /// <summary>
    /// 删除引用对象，返回当前引用对象个数，当值为零时可以安全调用 Close()
    /// </summary>
    public override int DelRefObj(object user)
    {
        _refObjs.Remove(user);
        return _refObjs.Count;
    }

    /// <inheritdoc />
    /// <summary>
    /// 判断端口是否打开
    /// </summary>
    public override bool IsOpened
    {
        get { return _isRunning; }
    }

    /// <summary>
    /// 数据接收回调处理函数
    /// </summary>
    private void SocketReceiveHandle()
    {
        // 接收数据处理线程
        while (_isRunning)
        {
            try
            {
                if ( _socket == null ) break;
                if (_socket.Poll(500, SelectMode.SelectRead))
                {
                    if (!_isRunning) break;
                    var len = _socket.Receive(_rcvData);
                    NotifyDataRcv(_rcvData, len);
                }
            }
            catch (Exception exp)
            {
                _isRunning = false;//Stop Socket Recv Thread
                log.Error($"UDP Error:{exp}");
            }
        }

        // 关闭处理
        if (null != _socket)
        {
            _socket.Shutdown(SocketShutdown.Both);
            _socket.Close();
            _socket.Dispose();
            _socket = null;
        }
    }
    /// <inheritdoc />
    /// <summary>
    /// 通过适配器发送数据
    /// </summary>
    public override void Write(string text)
    {
        if (!_isRunning) return;
        var bytes = Encoding.ASCII.GetBytes(text);
        lock (_locker)
        {
            _socket?.SendTo(bytes, bytes.Length, SocketFlags.None, _remoteEp);
        }
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        if (!_isRunning) return;
        lock (_locker)
        {
            _socket?.SendTo(buffer, offset, count, SocketFlags.None, _remoteEp);
        }
    }

    public override void Write(byte[] buffer)
    {
        if (!_isRunning) return;
        lock (_locker)
        {
            _socket?.SendTo(buffer, 0, buffer.Length, SocketFlags.None, _remoteEp);
        }
    }
}//end UdpClientAdapter
